package org.moonlight.jvm.instructions.references;

import org.moonlight.jvm.instructions.base.Index16Instruction;
import org.moonlight.jvm.rtda.exclusive.Frame;
import org.moonlight.jvm.rtda.exclusive.OperandStack;
import org.moonlight.jvm.rtda.share.heap.Class;
import org.moonlight.jvm.rtda.share.heap.Slots;
import org.moonlight.jvm.rtda.share.heap.classmember.Field;
import org.moonlight.jvm.rtda.share.heap.classmember.Method;
import org.moonlight.jvm.rtda.share.heap.symref.FieldRef;
import org.moonlight.jvm.rtda.share.methodarea.RtConstantPool;

/**
 * PutStatic: 给类的某个静态变量赋值，它需要两个操作数。
 *
 *           第一个操作数是uint16索引，来自字节码。
 *           通过这个索引可以从当前类的运行时常量池中找到一个字段符号引用，
 *           解析这个符号引用就可以知道要给类的哪个静态变量赋值。
 *
 *           第二个操作数是要赋给静态变量的值，从操作数栈中弹出
 *
 *           根据字段类型从操作数栈中弹出相应的值，然后赋给静态变量
 * @author Moonlight
 * @createTime 2023/8/30 14:33
 **/
public class PutStatic extends Index16Instruction {

    @Override
    public void execute(Frame frame) {
        // 当前方法
        Method curMethod = frame.getMethod();
        // 当前类
        Class curClazz = curMethod.getClazz();
        // 当前运行时常量池
        RtConstantPool rtCp = curClazz.getRtCp();
        // 通过 uint16索引 获取 字段符号引用
        FieldRef fieldRef = (FieldRef) rtCp.getConstant(this.idx);
        // 通过这个字段符号引用，找到对应的字段和类
        Field field = fieldRef.resolvedField();
        Class clazz = field.getClazz();
        // 如果类还没有经过类初始化，那么需要先经过类初始化
        // 类初始化时，先终止当前指令的执行，并将当前栈帧的nextPc重置指向当前指令
        if (!clazz.isInitStarted()) {
            frame.revertNextPc();
            clazz.classInit(frame.getThread());
            return;
        }
        // 如果解析后的字段是实例字段而非静态字段，则抛出IncompatibleClassChangeError异常。
        if (!field.isStatic()) {
            throw new IncompatibleClassChangeError();
        }
        // 如果是final字段，则实际操作的是静态常量，只能在类初始化方法中给它赋值。
        if (field.isFinal()) {
            if (!curClazz.equals(clazz) || !"<clinit>".equals(curMethod.getName())) {
                throw new IllegalAccessError();
            }
        }

        String desc = field.getDescriptor();
        int slotId = field.getSlotId();
        Slots staticVars = clazz.getStaticVars();
        OperandStack operandStack = frame.getOperandStack();

        switch (desc.substring(0, 1)) {
            case "Z":
            case "B":
            case "C":
            case "S":
            case "I":
                staticVars.setInt(slotId, operandStack.popInt());
                break;
            case "F":
                staticVars.setFloat(slotId, operandStack.popFloat());
                break;
            case "J":
                staticVars.setLong(slotId, operandStack.popLong());
                break;
            case "D":
                staticVars.setDouble(slotId, operandStack.popDouble());
                break;
            case "L":
            case "[":
                staticVars.setRef(slotId, operandStack.popRef());
                break;
            default:
                throw new RuntimeException("不支持的描述符: " + desc);
        }
    }

}
